"""
微信公众号服务 - 占位功能
"""

import ssl
import asyncio
import aiohttp
import json
import certifi
from typing import Dict, Any, Optional
from datetime import datetime, timedelta

from app.config import settings
from app.utils.logger import get_monitor_logger



class WeChatService:
    """微信公众号服务类 (占位功能) - 单例模式"""
    
    _instance = None
    _initialized = False
    
    def __new__(cls):
        """单例模式：确保每个进程只创建一个实例"""
        if cls._instance is None:
            cls._instance = super(WeChatService, cls).__new__(cls)
        return cls._instance
    
    def __init__(self):
        # 防止重复初始化
        if self._initialized:
            return
        
        self.app_id = settings.WECHAT_APP_ID
        self.app_secret = settings.WECHAT_APP_SECRET
        self.token = settings.WECHAT_TOKEN
        self.encoding_aes_key = settings.WECHAT_ENCODING_AES_KEY
        
        self.access_token = None
        self.access_token_expires_at = None
        
        # 🎯 小程序跳转相关配置
        self.use_miniprogram_jump = getattr(settings, 'WECHAT_USE_MINIPROGRAM_JUMP', True)  # 默认启用小程序跳转
        self.miniprogram_app_id = getattr(settings, 'WECHAT_MINIPROGRAM_APP_ID', None)
        
        self.logger = get_monitor_logger()
        
        # 初始化日志记录小程序配置
        import os
        process_id = os.getpid()
        self.logger.info(f"📱 微信服务初始化 [PID:{process_id}]: 小程序跳转={'启用' if self.use_miniprogram_jump else '禁用'}, AppID={self.miniprogram_app_id or '未配置'}")
        
        # 创建 SSL上下文
        self.ssl_context = self._create_ssl_context()
        
        # 标记已初始化
        WeChatService._initialized = True
        
    def _create_ssl_context(self) -> ssl.SSLContext:
        """
        创建自定义SSL上下文，解决云服务器SSL证书验证问题
        
        Returns:
            ssl.SSLContext: SSL上下文
        """
        try:
            # 使用certifi提供的CA证书包
            ssl_context = ssl.create_default_context(cafile=certifi.where())
            self.logger.info(f"✅ 使用certifi CA证书包: {certifi.where()}")
            return ssl_context
        except Exception as e:
            self.logger.warning(f"⚠️ 创建certifi SSL上下文失败: {e}")
            
            try:
                # 使用默认SSL上下文
                ssl_context = ssl.create_default_context()
                self.logger.info("✅ 使用默认SSL上下文")
                return ssl_context
            except Exception as e2:
                self.logger.error(f"❌ 创建默认SSL上下文失败: {e2}")
                
                # 创建不验证的SSL上下文（仅用于紧急情况）
                ssl_context = ssl.create_default_context()
                ssl_context.check_hostname = False
                ssl_context.verify_mode = ssl.CERT_NONE
                self.logger.warning("⚠️ 使用不验证SSL证书的上下文（不推荐生产环境）")
                return ssl_context
    
    def _create_http_connector(self, verify_ssl: bool = True) -> aiohttp.TCPConnector:
        """
        创建HTTP连接器
        
        Args:
            verify_ssl: 是否验证SSL证书
            
        Returns:
            aiohttp.TCPConnector: HTTP连接器
        """
        if verify_ssl:
            return aiohttp.TCPConnector(ssl=self.ssl_context)
        else:
            # 禁用SSL验证（仅用于紧急情况）
            return aiohttp.TCPConnector(ssl=False)
    
    async def get_access_token(self, force_refresh: bool = False) -> Optional[str]:
        """
        获取微信公众号access_token（支持Redis缓存和强制刷新）
        
        Args:
            force_refresh: 是否强制刷新token
            
        Returns:
            Optional[str]: access_token或None
        """
        try:
            # 导入Redis客户端
            from app.utils.redis_client import get_redis_client
            redis_client = get_redis_client()
            redis_key = f"wechat_access_token:{self.app_id}"
            redis_expires_key = f"wechat_access_token_expires:{self.app_id}"
            
            # 如果不是强制刷新，先检查Redis缓存
            if not force_refresh:
                try:
                    cached_token = redis_client.get(redis_key)
                    cached_expires = redis_client.get(redis_expires_key)
                    
                    if cached_token and cached_expires:
                        expires_at = datetime.fromisoformat(cached_expires.decode() if isinstance(cached_expires, bytes) else cached_expires)
                        if datetime.now() < expires_at:
                            self.access_token = cached_token.decode() if isinstance(cached_token, bytes) else cached_token
                            self.access_token_expires_at = expires_at
                            self.logger.debug(f"💾 使用Redis缓存的access_token，还有{(expires_at - datetime.now()).total_seconds():.0f}秒过期")
                            return self.access_token
                except Exception as cache_error:
                    self.logger.warning(f"⚠️ Redis缓存读取失败: {cache_error}")
            
            # 获取新的access_token - 使用稳定版API
            url = "https://api.weixin.qq.com/cgi-bin/stable_token"
            request_data = {
                "grant_type": "client_credential",
                "appid": self.app_id,
                "secret": self.app_secret,
                "force_refresh": force_refresh
            }
            
            self.logger.info(f"🔄 {'强制' if force_refresh else '普通'}模式获取access_token...")
            
            # 创建HTTP连接器
            connector = self._create_http_connector(verify_ssl=True)
            
            async with aiohttp.ClientSession(connector=connector) as session:
                async with session.post(url, json=request_data) as response:
                    data = await response.json()
                    
                    if "access_token" in data:
                        self.access_token = data["access_token"]
                        expires_in = data.get("expires_in", 7200)
                        # 提前5分钟过期，但最少保留300秒
                        buffer_time = min(300, expires_in // 4)
                        self.access_token_expires_at = datetime.now() + timedelta(seconds=expires_in - buffer_time)
                        
                        # 存入Redis缓存
                        try:
                            redis_client.set(redis_key, self.access_token, ex=expires_in)
                            redis_client.set(redis_expires_key, self.access_token_expires_at.isoformat(), ex=expires_in)
                            self.logger.info(f"✅ access_token已缓存到Redis，有效期{expires_in}秒")
                        except Exception as cache_error:
                            self.logger.warning(f"⚠️ Redis缓存存储失败: {cache_error}")
                        
                        refresh_mode = '强制' if force_refresh else ''
                        self.logger.info(f"✅ 微信access_token{refresh_mode}获取成功，有效期{expires_in}秒")
                        return self.access_token
                    else:
                        self.logger.error(f"❌ 微信access_token获取失败: {data}")
                        return None
                        
        except Exception as e:
            self.logger.error(f"❌ 获取微信access_token异常: {e}")
            
            # 如果SSL验证失败，尝试不验证SSL
            if "SSL" in str(e) or "certificate" in str(e).lower():
                self.logger.warning("⚠️ SSL验证失败，尝试禁用SSL验证...")
                try:
                    connector = self._create_http_connector(verify_ssl=False)
                    async with aiohttp.ClientSession(connector=connector) as session:
                        async with session.get(url, params=params) as response:
                            data = await response.json()
                            
                            if "access_token" in data:
                                self.access_token = data["access_token"]
                                expires_in = data.get("expires_in", 7200)
                                self.access_token_expires_at = datetime.now() + timedelta(seconds=expires_in - 300)
                                
                                self.logger.info(f"✅ 微信access_token获取成功（禁用SSL验证），有效期{expires_in}秒")
                                return self.access_token
                            else:
                                self.logger.error(f"❌ 微信access_token获取失败: {data}")
                                return None
                except Exception as e2:
                    self.logger.error(f"❌ 禁用SSL验证后仍然失败: {e2}")
            
            return None
    
    async def send_template_message(self, openid: str, template_id: str, 
                                  template_data: Dict[str, Any], url: str = "", 
                                  notify_log_id: Optional[int] = None) -> bool:
        """
        发送微信模板消息
        
        Args:
            openid: 用户openid
            template_id: 模板ID
            template_data: 模板数据
            url: 点击跳转URL（网页跳转）
            notify_log_id: 通知日志ID（小程序跳转）
            
        Returns:
            bool: 发送是否成功
        """
        try:
            access_token = await self.get_access_token()
            if not access_token:
                self.logger.error("❌ 无法获取access_token，微信消息发送失败")
                return False
            
            # 构建请求数据
            message_data = {
                "touser": openid,
                "template_id": template_id,
                "data": template_data
            }
            
            # 根据配置决定跳转方式
            if self.use_miniprogram_jump and notify_log_id and self.miniprogram_app_id:
                # 使用小程序跳转
                miniprogram_path = f"pages/notify-detail/notify-detail?logId={notify_log_id}"
                message_data["miniprogram"] = {
                    "appid": self.miniprogram_app_id,
                    "pagepath": miniprogram_path
                }
                self.logger.info(f"📱 使用小程序跳转: {miniprogram_path} (AppID: {self.miniprogram_app_id})")
            else:
                # 使用网页跳转（默认）
                message_data["url"] = url
                jump_reason = []
                if not self.use_miniprogram_jump:
                    jump_reason.append("未启用小程序跳转")
                if not notify_log_id:
                    jump_reason.append("缺少notify_log_id")
                if not self.miniprogram_app_id:
                    jump_reason.append("缺少小程序AppID")
                reason = ", ".join(jump_reason) if jump_reason else "默认设置"
                self.logger.info(f"🌐 使用网页跳转: {url[:50]}... (原因: {reason})")
            
            # 发送模板消息
            send_url = f"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={access_token}"
            
            # 创建HTTP连接器
            connector = self._create_http_connector(verify_ssl=True)
            
            async with aiohttp.ClientSession(connector=connector) as session:
                async with session.post(send_url, json=message_data) as response:
                    result = await response.json()
                    
                    if result.get("errcode") == 0:
                        self.logger.info(f"✅ 微信模板消息发送成功: {openid[:8]}***")
                        return True
                    elif result.get("errcode") == 40001:
                        # access_token无效，强制刷新并重试一次
                        self.logger.warning(f"⚠️ access_token无效，强制刷新并重试: {result}")
                        
                        # 强制刷新access_token
                        new_access_token = await self.get_access_token(force_refresh=True)
                        if not new_access_token:
                            self.logger.error("❌ 强制刷新access_token失败")
                            return False
                        
                        # 更新URL并重试发送
                        retry_send_url = f"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={new_access_token}"
                        async with session.post(retry_send_url, json=message_data) as retry_response:
                            retry_result = await retry_response.json()
                            
                            if retry_result.get("errcode") == 0:
                                self.logger.info(f"✅ 重试发送成功: {openid[:8]}***")
                                return True
                            else:
                                self.logger.error(f"❌ 重试发送仍失败: {retry_result}")
                                return False
                    else:
                        self.logger.error(f"❌ 微信模板消息发送失败: {result}")
                        return False
                        
        except Exception as e:
            self.logger.error(f"❌ 微信模板消息发送异常: {e}")
            
            # 如果SSL验证失败，尝试不验证SSL
            if "SSL" in str(e) or "certificate" in str(e).lower():
                self.logger.warning("⚠️ SSL验证失败，尝试禁用SSL验证发送消息...")
                try:
                    connector = self._create_http_connector(verify_ssl=False)
                    async with aiohttp.ClientSession(connector=connector) as session:
                        async with session.post(send_url, json=message_data) as response:
                            result = await response.json()
                            
                            if result.get("errcode") == 0:
                                self.logger.info(f"✅ 微信模板消息发送成功（禁用SSL验证）: {openid[:8]}***")
                                return True
                            else:
                                self.logger.error(f"❌ 微信模板消息发送失败: {result}")
                                return False
                except Exception as e2:
                    self.logger.error(f"❌ 禁用SSL验证后发送消息仍然失败: {e2}")
            
            return False
    
    async def send_product_notification(self, openid: str, products: list, 
                                      notification_subject: str, monitor_type: str = "keyword",
                                      template_id: str = None, notify_log_id: Optional[int] = None) -> bool:
        """
        发送商品上新通知（支持汇率换算和小程序跳转）
        
        Args:
            openid: 用户openid
            products: 商品列表
            notification_subject: 通知主题（关键词、卖家名或店铺名）
            monitor_type: 监控类型（keyword/seller/shop）
            template_id: 模板ID
            notify_log_id: 通知日志ID（用于小程序跳转）
            
        Returns:
            bool: 发送是否成功
        """
        try:
            if not template_id:
                template_id = "AJaagR3LB3av6A34jdJ2Zi4PJ-UYaFRUx42JrW4-kc0"  # 工单完成通知模板
            
            if not products:
                self.logger.warning("❌ 商品列表为空，无法发送通知")
                return False
                
            # 获取第一个商品信息
            first_product = products[0]
            product_name = first_product.get('name', '未知商品')
            product_price = first_product.get('price', 0)
            product_url = first_product.get('url', '')
            
            # 格式化价格显示（日元 + 人民币）- 简化版本
            try:
                # 使用固定汇率0.0485进行换算
                rate = 0.0485
                cny_price = product_price * rate
                
                # 格式化显示
                if product_price >= 10000:
                    jpy_display = f"¥{product_price:,.0f}"
                    cny_display = f"¥{cny_price:,.0f}"
                else:
                    jpy_display = f"¥{product_price:.0f}"
                    cny_display = f"¥{cny_price:.0f}"
                
                price_display = f"{jpy_display} | {cny_display}"
                self.logger.info(f"💱 汇率换算: ¥{product_price} JPY → {price_display}")
                
            except Exception as e:
                self.logger.error(f"❌ 汇率换算失败，使用默认价格: {e}")
                price_display = f"¥{product_price}"
            
            # 根据监控类型确定项目名称
            if monitor_type == "seller":
                project_name = f"卖家监控: {notification_subject}"
            elif monitor_type == "shop":
                project_name = f"店铺监控: {notification_subject}"
            else:
                project_name = f"关键词监控: {notification_subject}"
            
            # 构建模板数据 - 映射到工单模板字段
            template_data = {
                "thing7": {  # 工单名称 -> 商品名称
                    "value": product_name[:20],  # 微信模板限制20个字符
                    "color": "#173177"
                },
                "thing18": {  # 项目名称 -> 监控类型
                    "value": project_name[:20],  # 微信模板限制20个字符
                    "color": "#173177"
                },
                "thing2": {  # 产品名称 -> 价格信息（支持汇率换算）
                    "value": price_display,
                    "color": "#FF6B35"
                },
                "time5": {  # 完成时间 -> 当前时间
                    "value": datetime.now().strftime("%Y年%m月%d日 %H:%M"),
                    "color": "#173177"
                }
            }
            
            if notify_log_id:
                self.logger.info(f"📱 发送微信模板消息: OpenID={openid[:8]}***, 商品={product_name[:10]}..., 价格={price_display}, logId={notify_log_id}")
            else:
                self.logger.info(f"📱 发送微信模板消息: OpenID={openid[:8]}***, 商品={product_name[:10]}..., 价格={price_display}")
            
            return await self.send_template_message(openid, template_id, template_data, product_url, notify_log_id)
            
        except Exception as e:
            self.logger.error(f"❌ 发送商品通知异常: {e}")
            return False
    
    async def send_expiry_notification(self, openid: str, monitor_name: str, 
                                      expires_at: str, monitor_type: str = "keyword",
                                      template_id: str = None) -> bool:
        """
        发送监控过期通知
        
        Args:
            openid: 用户openid
            monitor_name: 监控任务名称（关键词或卖家名）
            expires_at: 过期时间字符串
            monitor_type: 监控类型（keyword/seller/shop）
            template_id: 模板ID
            
        Returns:
            bool: 发送是否成功
        """
        try:
            if not template_id:
                template_id = "gx0Zas4-m2JFaT4lgkVR1Pqa9VsQ1uBDsJtmA5Ow614"  # 订阅过期提醒模板
            
            # 根据监控类型格式化产品名称
            if monitor_type == "seller":
                product_name = f"卖家监控: {monitor_name}"
            elif monitor_type == "shop":
                product_name = f"店铺监控: {monitor_name}"
            else:
                product_name = f"关键词监控: {monitor_name}"
            
            # 格式化过期时间
            try:
                from datetime import datetime
                if isinstance(expires_at, str):
                    expire_dt = datetime.fromisoformat(expires_at.replace('Z', '+00:00'))
                    formatted_time = expire_dt.strftime("%Y年%m月%d日 %H:%M")
                else:
                    formatted_time = expires_at
            except Exception as e:
                self.logger.warning(f"⚠️ 时间格式化失败: {e}, 使用原始值")
                formatted_time = str(expires_at)
            
            # 构建模板数据 - 映射到订阅过期模板字段
            template_data = {
                "thing20": {  # 产品名称 -> 监控任务名称
                    "value": product_name[:20],  # 微信模板限制20个字符
                    "color": "#173177"
                },
                "time6": {  # 到期时间
                    "value": formatted_time,
                    "color": "#FF6B35"
                }
            }
            
            self.logger.info(f"🚨 发送监控过期通知: OpenID={openid[:8]}***, 监控={product_name}, 过期时间={formatted_time}")
            
            # 📝 过期通知需要直接发送模板消息，使用 miniprogram 字段
            try:
                access_token = await self.get_access_token()
                if not access_token:
                    self.logger.error("❌ 无法获取access_token，过期通知发送失败")
                    return False
                
                # 构建请求数据
                message_data = {
                    "touser": openid,
                    "template_id": template_id,
                    "data": template_data
                }
                
                # 根据配置决定跳转方式
                if self.use_miniprogram_jump and self.miniprogram_app_id:
                    # 使用小程序跳转到订阅页面
                    miniprogram_path = "pages/subscribe/subscribe"
                    message_data["miniprogram"] = {
                        "appid": self.miniprogram_app_id,
                        "pagepath": miniprogram_path
                    }
                    self.logger.info(f"📱 过期通知使用小程序跳转: {miniprogram_path} (AppID: {self.miniprogram_app_id})")
                else:
                    # 使用网页跳转到订阅页面
                    web_url = "https://your-domain.com/subscribe"
                    message_data["url"] = web_url
                    reason = []
                    if not self.use_miniprogram_jump:
                        reason.append("未启用小程序跳转")
                    if not self.miniprogram_app_id:
                        reason.append("缺少小程序 AppID")
                    reason_text = ", ".join(reason) if reason else "默认设置"
                    self.logger.info(f"🌐 过期通知使用网页跳转: {web_url} (原因: {reason_text})")
                
                # 发送模板消息
                send_url = f"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={access_token}"
                connector = self._create_http_connector(verify_ssl=True)
                
                async with aiohttp.ClientSession(connector=connector) as session:
                    async with session.post(send_url, json=message_data) as response:
                        result = await response.json()
                        
                        if result.get("errcode") == 0:
                            self.logger.info(f"✅ 过期通知模板消息发送成功: {openid[:8]}***")
                            return True
                        else:
                            self.logger.error(f"❌ 过期通知模板消息发送失败: {result}")
                            return False
                            
            except Exception as send_error:
                self.logger.error(f"❌ 过期通知发送异常: {send_error}")
                return False
            
        except Exception as e:
            self.logger.error(f"❌ 发送过期通知异常: {e}")
            return False
    
    def verify_signature(self, signature: str, timestamp: str, nonce: str) -> bool:
        """
        验证微信服务器签名
        
        Args:
            signature: 微信加密签名
            timestamp: 时间戳
            nonce: 随机数
            
        Returns:
            bool: 验证是否通过
        """
        try:
            import hashlib
            
            # 将token、timestamp、nonce三个参数进行字典序排序
            tmp_list = [self.token, timestamp, nonce]
            tmp_list.sort()
            tmp_str = ''.join(tmp_list)
            
            # sha1加密
            sha1 = hashlib.sha1()
            sha1.update(tmp_str.encode('utf-8'))
            hashcode = sha1.hexdigest()
            
            # 验证签名
            if hashcode == signature:
                self.logger.info("✅ 微信服务器签名验证通过")
                return True
            else:
                self.logger.warning("⚠️ 微信服务器签名验证失败")
                return False
                
        except Exception as e:
            self.logger.error(f"❌ 微信签名验证异常: {e}")
            return False


# 全局微信服务实例
wechat_service = WeChatService()


async def send_wechat_product_notification(openid: str, products: list, 
                                         notification_subject: str, monitor_type: str = "keyword") -> bool:
    """
    发送微信商品通知的便捷函数（支持汇率换算）
    
    Args:
        openid: 用户openid
        products: 商品列表
        notification_subject: 通知主题（关键词、卖家名或店铺名）
        monitor_type: 监控类型（keyword/seller/shop）

        
    Returns:
        bool: 发送是否成功
    """
    if not wechat_service.app_id or not wechat_service.app_secret:
        wechat_service.logger.warning("⚠️ 微信公众号未配置，跳过微信通知")
        return False
    
    return await wechat_service.send_product_notification(openid, products, notification_subject, monitor_type)
